import time

from machine import Pin, SoftSPI
from tft import st7789py as st  # TFT屏幕驱动程序
from tft import tft_config  # TFT屏幕配置
from lib import seniverse_api  # 获取气象信息
from lib.wifi import WIFI  # WIFI连接助手
from lib.dispatcher import Dispatcher  # 定时器管理
from lib import ntptime  # 获取网络时间


class DisplayManager:
    """
    使用ESP32S3+2.8吋 TFT屏幕制作一个天气时钟
    
    功能：
        - 显示时钟的IP地址，允许手机联网设置WIFI账号密码
        - 断网自动重连
        - 联网矫正本地RTC的时间
        - 获取气象信息，展示温度、天气等数据
        - 显示时钟信息：星期、日期、时间
    """

    def clear_screen(self):
        """
        清除屏幕上的所有内容。
        """
        self.tft.fill(st.WHITE)

    def __init__(self, tft, wifi: WIFI, api_key, location):
        """
        初始化函数，设置显示模块、Wi-Fi模块、API密钥、位置信息。

        Args:
            tft (object): 显示模块对象，用于信息显示
            wifi (object): Wi-Fi模块对象，用于网络连接
            api_key: 天气API密钥，用于访问天气信息
            location (str): 位置信息，用于获取特定地区的天气
        """
        self.tft = tft  # 显示模块对象
        self.wifi = wifi  # Wi-Fi模块对象        
        # https://www.seniverse.com/,实际应用中，需要自行申请API_KEY
        self.api_key = api_key  # 天气API密钥
        self.location = location  # 位置信息
        self.last_time = ""  # 初始化最后一次获取天气信息的时间
        self.date_str = ""  # 初始化日期字符串
        self.week_str = ""  # 初始化星期字符串

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.tft.spi.deinit()
        self.wifi.disconnect()

    def show_time(self, t):
        """
        更新屏幕上的时间显示。

        Args:
            t (tuple): 当前时间的元组，格式为 (year, month, day, hour, minute, second, weekday, yearday)。
        """
        year, month, day, hour, minute, second, week = t[0:7]

        time_str = f"{hour:02}:{minute:02}:{second:02}"
        date_str = f"{month:02}/{day:02}"
        week_arr = ["日", "一", "二", "三", "四", "五", "六"]
        week_str = f"星期{week_arr[week]}"

        if week_str != self.week_str:
            self.tft.text(24, week_str, 20, 160, st.RED, st.WHITE)
            self.week_str = week_str

        if date_str != self.date_str:
            # 英文数字没有32号字体，默认使用16号字体
            self.tft.text(16, date_str, 20, 200, st.BLUE, st.WHITE)
            self.date_str = date_str

        # 48号字体只定义了时钟显示需要的字符
        if time_str != self.last_time:
            self.tft.text(48, time_str, 20, 240, st.RED, st.WHITE)
            self.last_time = time_str

    def show_weather(self):
        try:
            response = seniverse_api.Weather_now.request(key=self.api_key, location=self.location)
            weather_data = seniverse_api.Weather_now.analysis(response)
            print(weather_data)

            # 需要使用code作为图片的文件名
            code = weather_data.get("code", "0")
            text = weather_data.get("text", "未知")
            temperature = weather_data.get("temperature", "未知")
            location = self.location

            self.tft.text(24, f"{location} {text}", 20, 30, st.BLACK, st.WHITE)
            self.tft.text(32, f"{temperature}℃", 20, 90, st.BLUE, st.WHITE)

            # 根据code检索对应的图像，显示
            # pictures目录下已经包含心知天气官方的天气代码图标，
            # 项目只支持24位的BMP图片，已做转换，目前是白底的图片，可以转换为透明图片。
            self.tft.display_bmp(f"pictures/{code}.bmp", 150, 64)
        except Exception as e:
            print(f"Error in show_weather: {e}")

    def run(self):
        self.clear_screen()

        def task0():
            """
            任务0：检查并重新连接WiFi，然后尝试通过NTP服务器更新时间

            此函数首先检查WiFi是否已连接，如果未连接，则尝试重新连接WiFi
            成功连接WiFi后，尝试通过指定的NTP服务器获取并设置当前时间
            """
            try:
                # 检查WiFi是否已连接
                if not self.wifi.isconnected():
                    print("断网重连中...")
                    self.wifi.connect()

                # 再次检查WiFi是否已连接
                if self.wifi.isconnected():
                    try:
                        # 从NTP服务器获取当前时间并设置系统时间
                        t = ntptime.time(host="ntp.aliyun.com")
                        # 设置系统时间
                        ntptime.settime(t)
                    except Exception as e:
                        # 处理设置NTP时间时发生的异常
                        print(f"Failed to set NTP time: {e}")
            except Exception as e:
                # 处理连接WiFi时发生的异常
                print(f"Failed to connect to WiFi: {e}")

        def task1():
            # 显示时间
            t = time.localtime(time.time())
            self.show_time(t)

        def task2():
            # 显示天气信息
            try:
                self.show_weather()
            except Exception as e:
                print(f"Error in task2: {e}")

        # 创建调度器，并添加三个任务
        tasks = Dispatcher()
        # 添加三个任务
        tasks.add_work(task0, 5000)  # 每分钟同步一次时间
        tasks.add_work(task1, 1000)  # 每秒更新一次时间
        tasks.add_work(task2, 5000)  # 每10分钟更新一次天气


def main():
    # Wi-Fi信息
    ssid = "chuitiao"
    password = "12345678"

    # https://www.seniverse.com/
    # 实际应用中，需要自行申请API_KEY
    api_key = "SVa3S0UKHIy9dDArs"  # 心知天气的API_KEY，附带的KEY只用于测试，每天调用的次数有限制
    location = "番禺"

    try:
        wifi = WIFI(ssid, password)

        # 直接联网会有延时，体验不好，系统通过断网重连功能自动触发联网
        # wifi.connect()

        with tft_config.config(0) as tft, DisplayManager(tft, wifi, api_key, location) as dm:
            dm.run()
    except Exception as e:
        print(f"Error in main: {e}")


if __name__ == "__main__":
    main()
